// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <stdlib.h>

#include "runtime_types.h"
#include "print.h"
#include "util.h"
#include "proc.h"
#include "backtrace.h"
#include "panic.h"
#include "defer.h"
#include "mem_alloc.h"
#include "lock.h"
#include "atomic.h"
#include "string.h"

// Print the panic stack.  This is used when there is no recover.

static void
printpanics(struct __go_panic_stack *p)
{
	if (p->__next != NULL) {
		printpanics(p->__next);
		runtime_printf("\t");
	}
	runtime_printf("panic: ");
	runtime_printany(p->__arg);
	if (p->__was_recovered)
		runtime_printf(" [recovered]");
	runtime_printf("\n");
}

static uint32 panicking;
static Lock paniclk;
static Lock stopsign;

bool
runtime_panicking() {
	return panicking != 0;
}

static void
runtime_startpanic(void)
{
/* TODO

	M *m;

	m = runtime_m();
	if(m->dying) {
		runtime_printf("panic during panic\n");
		runtime_exit(3);
	}
	m->dying = 1;
*/
	runtime_xadd(&panicking, 1);
	runtime_lock(&paniclk);
}

static void runtime_dopanic(int32) __attribute__ ((noreturn));

static void
runtime_dopanic(int32 unused __attribute__ ((unused)))
{
	G* g;
	static bool didothers;

	g = runtime_g();

	if(runtime_gotraceback()){
		runtime_printf("\n");
		runtime_goroutineheader(g);
		runtime_traceback();
		runtime_goroutinetrailer(g);

		if(!didothers) {
			didothers = true;
			runtime_tracebackothers(g);
		}
	}

	runtime_unlock(&paniclk);
	if(runtime_xadd(&panicking, -1) != 0) {
		// Some other m is panicking too.
		// Let it print what it needs to print.
		// Wait forever without chewing up cpu.
		// It will exit when it's done.
		runtime_lock(&stopsign);
		runtime_lock(&stopsign);
	}

	// FIXME: somehow exit() doesn't work right
	runtime_lock(&stopsign);
	runtime_lock(&stopsign);

	exit(2);
}

// This implements __go_panic which is used for the panic function.

void
__go_panic(struct __go_empty_interface arg)
{
	G *g;
	struct __go_panic_stack *n;

	g = runtime_g();

	n = (struct __go_panic_stack *)runtime_malloc(sizeof(struct __go_panic_stack));
	n->__arg = arg;
	n->__next = g->panic;
	g->panic = n;

	// Run all the defer functions.

	while (1) {
		struct __go_defer_stack *d;
		void (*pfn) (void *);

		d = g->defer;
		if (d == NULL)
			break;

		pfn = d->__pfn;
		d->__pfn = NULL;

		if (pfn != NULL) {
			(*pfn)(d->__arg);

			if (n->__was_recovered) {
				// Some defer function called recover.
				// That means that we should stop running this panic.

				// FIXME: Since we don't actually know how to unwind,
				// we ignore the recover().

				runtime_printf("Warning: recover() attempted, but not supported.");
			}

			// Because we executed that defer function by a panic, and
			// it did not call recover, we know that we are not
			// returning from the calling function--we are panicing
			// through it.
			*d->__frame = 0;
		}

		g->defer = d->__next;
		runtime_free(d);
	}

	// The panic was not recovered.

	runtime_startpanic();
	printpanics(g->panic);
	runtime_dopanic(0);
}

// This is called by a thunk to see if the real function should be
// permitted to recover a panic value.  Recovering a value is
// permitted if the thunk was called directly by defer.  RETADDR is
// the return address of the function which is calling
// __go_can_recover--this is, the thunk.

_Bool __go_can_recover(const void *retaddr)
{
	G *g;
	struct __go_defer_stack *d;
	const char *ret;
	const char *dret;

	g = runtime_g();

	d = g->defer;
	if (d == NULL)
		return 0;

	// The panic which this function would recover is the one on the top
	// of the panic stack.  We do not want to recover it if that panic
	// was on the top of the panic stack when this function was
	// deferred.
	if (d->__panic == g->panic)
		return 0;

	// D->__RETADDR is the address of a label immediately following the
	// call to the thunk.  We can recover a panic if that is the same as
	// the return address of the thunk.  We permit a bit of slack in
	// case there is any code between the function return and the label,
	// such as an instruction to adjust the stack pointer.

	ret = (const char *)retaddr;

#ifdef __sparc__
	// On SPARC the address we get, from __builtin_return_address, is
	// the address of the call instruction.  Adjust forward, also
	// skipping the delayed instruction following the call.
	ret += 8;
#endif

	dret = (const char *)d->__retaddr;
	return ret <= dret && ret + 16 >= dret;
}

// This is only called when it is valid for the caller to recover the
// value on top of the panic stack, if there is one.

struct __go_empty_interface __go_recover()
{
	G *g;
	struct __go_panic_stack *p;

	g = runtime_g();

	if (g->panic == NULL || g->panic->__was_recovered) {
		struct __go_empty_interface ret;

		ret.__type_descriptor = NULL;
		ret.__object = NULL;
		return ret;
	}
	p = g->panic;
	p->__was_recovered = 1;
	return p->__arg;
}

// This is called when a call to recover is deferred.  That is,
// something like
//   defer recover()
//
// We need to handle this specially.  In 6g/8g, the recover function
// looks up the stack frame.  In particular, that means that a
// deferred recover will not recover a panic thrown in the same
// function that defers the recover.  It will only recover a panic
// thrown in a function that defers the deferred call to recover.
//
// In other words:
//
// func f1() {
//	defer recover()	// does not stop panic
//	panic(0)
// }
//
// func f2() {
//	defer func() {
//		defer recover()	// stops panic(0)
//	}()
//	panic(0)
// }
//
// func f3() {
//	defer func() {
//		defer recover()	// does not stop panic
//		panic(0)
//	}()
//	panic(1)
// }
//
// func f4() {
//	defer func() {
//		defer func() {
//			defer recover()	// stops panic(0)
//		}()
//		panic(0)
//	}()
//	panic(1)
// }
//
// The interesting case here is f3.  As can be seen from f2, the
// deferred recover could pick up panic(1).  However, this does not
// happen because it is blocked by the panic(0).
//
// When a function calls recover, then when we invoke it we pass a
// hidden parameter indicating whether it should recover something.
// This parameter is set based on whether the function is being
// invoked directly from defer.  The parameter winds up determining
// whether __go_recover or __go_deferred_recover is called at all.
//
// In the case of a deferred recover, the hidden parameter which
// controls the call is actually the one set up for the function which
// runs the defer recover() statement.  That is the right thing in all
// the cases above except for f3.  In f3 the function is permitted to
// call recover, but the deferred recover call is not.  We address
// that here by checking for that specific case before calling
// recover.  If this function was deferred when there is already a
// panic on the panic stack, then we can only recover that panic, not
// any other.
//
// Note that we can get away with using a special function here
// because you are not permitted to take the address of a predeclared
// function like recover.

struct __go_empty_interface __go_deferred_recover()
{
	G *g;

	g = runtime_g();
	if (g->defer == NULL || g->defer->__panic != g->panic) {
		struct __go_empty_interface ret;

		ret.__type_descriptor = NULL;
		ret.__object = NULL;
		return ret;
	}
	return __go_recover();
}

void
runtime_panicstring(const char *s)
{
	Eface err;
	runtime_newErrorString(runtime_gostringnocopy((const byte*)s), &err);
	__go_panic(err);
}

void
runtime_throw(const char *s)
{
	runtime_startpanic();
	runtime_printf("throw: %s\n", s);
	
	runtime_dopanic(0);
	exit(1);	// not reached
}
